#include <stdio.h>
#include <stdlib.h>

#define QUEUE_MAX_SIZE 100 // 栈最多支持存放100个数据元素

/// 目前考虑按模拟线程按顺序到达

typedef struct Pcb {            // 创建进程对象
    int pID;                // 进程ID
    char pName[6];          // 进程名字， 使用字符，%s输出，若·pName，%c输出
    int arrivalTime;        // 到达时刻
    int serviceTime;        // 服务时长
    int waitTime;           // 等待时长（可以不用，降低难度）
    int servedTime;         // 已服务时间
} PCB;

typedef struct {            // 封装
    int procNum;            // 进程·数量
    int serviceTotalTime;   // 服务时长
    int timeSlice;          // 时间片
} ARGS;

typedef struct Queue {
    int elements[QUEUE_MAX_SIZE];
    int size;
} Queue;

/// rr轮转部分
void InputArgsProcInfo( ARGS *args );
void Getinfo(PCB *pcbs, ARGS args);
void MoniRR(Queue *q, PCB *pcbs, ARGS args);//获取数据，用户手动输入

/// 队列部分
// 初始化队列。初始化成功返回0；初始化失败返回1。
int init_queue(Queue ** q);
// 在队列q的尾巴上增加一个元素，其值为value。增加成功，返回0；增加失败，返回1
int enqueue (Queue * q, int value);
// 从队列q上摘取（去掉）一个元素，将其值放入value中。摘取成功，返回0；摘取失败，返回1。
int dequeue(Queue * q, int *value);
// 将数据重新加入在队列尾部。增加成功，返回0；增加失败，返回1
int re_enqueue(Queue * q, int value);
// 判断队列q是否为空。为空，返回0；不为空，返回1。
int isEmpty(Queue * q);
// 返回队列q中的元素个数。
//int size(Queue * q);



int main() {
    Queue *q = (Queue *) malloc(sizeof (Queue));
    PCB *pcbs =(PCB *) malloc(sizeof (PCB)) ;      // 做成局部变量,需要去分配动态地址, 存放进程信息
    ARGS args;      // 定义一个结构变量

    init_queue(&q);                        //  初始化

    //传出来  进程数 &args->procNum， 服务总时长 &args->serviceTotalTime， 时间片 &args->timeSlice，(地址)
//    InputArgsProcInfo(&args);
    args.procNum=3;
    args.timeSlice =1;

    Getinfo( pcbs, args);

    MoniRR( q,  pcbs,  args);

        return 0;
    }




//获取用户输入
void InputArgsProcInfo(ARGS *args) {
    //设定参数
    printf("----- 亲，要设置参数哦-----\n");
    //进程数
    printf("创建几个进程呢?(2~9):");
    scanf("%d", &args->procNum);
    //服务总时长
//    printf("一共要服务多少时长呢？(%d~%d):", args->procNum, args->procNum * 4);
//    scanf( "%d", &args->serviceTotalTime );
    //时间片
    printf("时间片呢？(1~%d):", args->procNum * 4);
    scanf("%d", &args->timeSlice);
}

//得到模拟进程信息
void Getinfo(PCB *pcbs, ARGS args){
    char N = 65;
    char Str;

    int i;
    int j;
    //进行模拟信息采集
    for (i = 0; i < args.procNum; ++i) {
        Str = N + i;
        pcbs[i].pName[0] = Str;
        pcbs[i].pID = i + 1;
        printf("输入进程 %c 数据(到达时间 服务时长):", pcbs[i].pName[0]);
        scanf("%d", &pcbs[i].arrivalTime);
        scanf(" %d", &pcbs[i].serviceTime);
    }

    printf("\n");
    printf("进程名    进程ID    到达时刻    服务时长\n");
    for (j = 0; j < args.procNum; j++) {
        printf("  %c        %d        %d         %d \n", pcbs[j].pName[0], pcbs[j].pID, pcbs[j].arrivalTime, pcbs[j].serviceTime);
    }
}

//主要轮转算法实现
void MoniRR(Queue *q, PCB *pcbs, ARGS args) {

    int Timeline1 = 0;
    int Timeline = 0; //引入一条时间线
    int val;
    char dequeue_record[100];           // 出队记录
    int m = 0;          // 出队记录
    int y;          // 定义一个时间片
    int x;         //表示服务时间剩余
    int key = 0;

    for (int a = 0; a < args.procNum; a++) {
        key = key + pcbs[a].serviceTime;
    }
    printf("本次模拟总时长为 %d \n", key);

    while (Timeline < key) {
        if (Timeline > key ) {
            //当进入队列的进程个数为总进程数n时且队列为空时退出循环
            //表示所有进程全部运行结束
            printf("已退出！！\n");
            break;
        }

        //Timeline为全局时间轴，增长为时间片，y以Timeline为基础，增加量为1，
        // 增加到Timeliness+时间片进行查找需要入队元素是否在执行时间内到达
        for (y = 0; y <  args.timeSlice; ++y) {
            if (pcbs[y].arrivalTime == y) { //当到达时间和Timeline1相等，进行入队
                //每次需要遍历所有进程到达时间，可能有些进程到达时间一样，依次入队
                printf("进程 %c在 %d 秒到达\n", pcbs[y].pName[0], y);
                enqueue(q, pcbs[y].pID);
            }
           }

        // 对队首元素进行 “服务”
        // 将队首元素加入数组
        for (Timeline = Timeline + args.timeSlice; Timeline < key+1; Timeline += args.timeSlice) {
            for (int i = 0; i < q->size; ++i) {
                if (isEmpty(q) == 1){
                printf("有\n");
                if (pcbs[i].pID == q->elements[0]) {
                    x = pcbs[i].serviceTime - args.timeSlice;
                    printf("进程 %d 剩余 %d秒\n", q->elements[0], x);
                    pcbs[i].serviceTime = x;
                    pcbs[i].pName[0] = dequeue_record[m];
                    m++;
                }}

                // x > 0,表示将重新进入队列·
                printf("   %d\n", x);
                if (x > 0) {
                    for (int i = y; i < y + args.timeSlice+1; ++i) {
                        printf("进程 %c在 %d 秒到达\n", pcbs[y].pName[0], y);
                        enqueue(q, pcbs[i].pID);
                    }
                    dequeue(q, &val);
                    printf("去掉队首模拟程序ID：%d\n", val);
                    Timeline += args.timeSlice; //该时间点某模拟程序出队、
                    re_enqueue(q, val); //重新入队
                } else {
                    dequeue(q, &val);
                    Timeline += args.timeSlice; //该时间点某模拟程序出队、
                    // 出队并用一个数组记录
                    dequeue_record[m] = val;
                    m++;
                    Timeline += args.timeSlice;
                    for (int i = 0; i < args.procNum; ++i) {
                        if (pcbs[i].pID == val) {
                            printf("在 %d 秒 进程 %c 完成轮转！\n", y, pcbs[i].pName[0]);
                            break;
                        }
                    }
                }
            }
            x = 0;
        }
    }
}



// 初始化队列。初始化成功返回0；初始化失败返回1。
int init_queue(Queue ** q){
    *q=(Queue *)malloc(sizeof(Queue));
    if((*q) != NULL){
        (*q)->size = 0;
        return 0;
    }else{
        return 1;
    }
}

// 判断队列q是否为空。为空，返回0；不为空，返回1。
int isEmpty(Queue * q){
    if (q->size == 0){
        printf("栈为空！\n");
        return 0;
    }
    return 1;
}

// 判断队列q是否已满。已满，返回0；不满，返回1。
int isFull(Queue * q){
    if (q->size >= QUEUE_MAX_SIZE){
        printf("栈已满！\n");
        return 0;
    }
    return 1;
}

// 在队列q的尾巴上增加一个元素，其值为value。增加成功，返回0；增加失败，返回1
int enqueue (Queue * q,  int value){
    if (isFull(q)){
        q->elements[q->size] = value;
        printf(" 此时队列有:");
        for (int i = 0; i < q->size+1; ++i) {
            printf(" %d ", q->elements[i]);
        }
        printf("\n");
        q->size++;
        return 0;
    }
    return 1;

}

// 将数据重新加入在队列尾部。增加成功，返回0；增加失败，返回1
int  re_enqueue(Queue * q, int value){
    if (isFull(q)){
        int  Va = q->elements[0];
        int sum = q->size - 1;
        for (int i = 0; i < sum; ++i) {
            q->elements[i] = q->elements[i+1];
        }
        q->elements[q->size] = Va;
        return 0;
    }
    return 1;

}

// 从队列q上摘取（去掉）一个元素，将其值放入value中。摘取成功，返回0；摘取失败，返回1。
int dequeue(Queue * q, int *value){
    if(isEmpty(q)){
        *value = q->elements[0];
        int sum = q->size - 1;
        for(int i=0;i<sum;i++){
            q->elements[i] = q->elements[i+1];
        }
        q->size--;
        return 0;
    }
    return 1;
}

// 返回队列q中的元素个数。
int size(Queue * q){
    return q->size;
}